---
title: "系統程式設計基礎"
description: "從 JavaScript 視角學習 C 語言系統程式設計，理解行程、執行緒、信號、系統呼叫，並對比 JavaScript 執行時環境。"
---

# 系統程式設計基礎

## 1. 概念介紹

### 從 JavaScript 執行時到 C 系統程式設計

在 JavaScript 中，你在執行時環境中工作，該環境抽象了系統級操作。而在 C 語言中，你可以直接存取作業系統，允許你建立行程、處理信號和進行系統呼叫。

> 💡 **核心概念**：C 系統程式設計給你對作業系統的底層控制，使你能夠建構系統工具、守護程序和高效能應用程式。

## 2. 行程和行程管理

<UniversalEditor title="行程建立" compare={true}>
```javascript !! js
// JavaScript - 行程管理 (Node.js)
const { spawn, exec } = require('child_process');

// 產生新行程
const child = spawn('ls', ['-la'], {
    stdio: 'inherit'
});

child.on('close', (code) => {
    console.log(`子行程退出，程式碼: ${code}`);
});

// 執行命令
exec('echo "來自子行程的問候"', (error, stdout, stderr) => {
    if (error) {
        console.error(`錯誤: ${error}`);
        return;
    }
    console.log(`輸出: ${stdout}`);
});

// 行程資訊
console.log('行程 ID:', process.pid);
console.log('父行程 ID:', process.ppid);
console.log('平台:', process.platform);
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int main() {
    pid_t pid;
    
    printf("父行程 ID: %d\n", getpid());
    
    // 建立新行程
    pid = fork();
    
    if (pid < 0) {
        // fork 失敗
        perror("fork 失敗");
        exit(1);
    } else if (pid == 0) {
        // 子行程
        printf("子行程 ID: %d\n", getpid());
        printf("子行程的父行程 ID: %d\n", getppid());
        
        // 執行命令
        execlp("ls", "ls", "-la", NULL);
        
        // 如果 execlp 返回，說明有錯誤
        perror("execlp 失敗");
        exit(1);
    } else {
        // 父行程
        printf("父行程等待子行程 %d\n", pid);
        
        int status;
        wait(&status);
        
        if (WIFEXITED(status)) {
            printf("子行程退出，狀態: %d\n", WEXITSTATUS(status));
        }
    }
    
    return 0;
}
```
</UniversalEditor>

## 3. 執行緒和並發

<UniversalEditor title="執行緒" compare={true}>
```javascript !! js
// JavaScript - 執行緒 (Node.js Worker Threads)
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

if (isMainThread) {
    // 主執行緒
    const worker = new Worker(__filename, {
        workerData: { number: 42 }
    });
    
    worker.on('message', (result) => {
        console.log('來自工作執行緒的結果:', result);
    });
    
    worker.on('error', (error) => {
        console.error('工作執行緒錯誤:', error);
    });
    
    worker.on('exit', (code) => {
        if (code !== 0) {
            console.error(`工作執行緒停止，退出程式碼: ${code}`);
        }
    });
} else {
    // 工作執行緒
    const result = workerData.number * 2;
    parentPort.postMessage(result);
}

// 非同步/等待並發
async function concurrentTasks() {
    const promises = [
        new Promise(resolve => setTimeout(() => resolve('任務 1'), 1000)),
        new Promise(resolve => setTimeout(() => resolve('任務 2'), 500)),
        new Promise(resolve => setTimeout(() => resolve('任務 3'), 1500))
    ];
    
    const results = await Promise.all(promises);
    console.log('所有任務完成:', results);
}

concurrentTasks();
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

// 執行緒函數
void* threadFunction(void* arg) {
    int threadId = *(int*)arg;
    printf("執行緒 %d 開始\n", threadId);
    
    // 模擬工作
    sleep(1);
    
    printf("執行緒 %d 結束\n", threadId);
    return NULL;
}

// 帶共享資料的執行緒
int sharedCounter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* incrementCounter(void* arg) {
    int iterations = *(int*)arg;
    
    for (int i = 0; i < iterations; i++) {
        pthread_mutex_lock(&mutex);
        sharedCounter++;
        pthread_mutex_unlock(&mutex);
    }
    
    return NULL;
}

int main() {
    pthread_t threads[3];
    int threadIds[3] = {1, 2, 3};
    
    // 建立執行緒
    for (int i = 0; i < 3; i++) {
        if (pthread_create(&threads[i], NULL, threadFunction, &threadIds[i]) != 0) {
            perror("建立執行緒失敗");
            return 1;
        }
    }
    
    // 等待執行緒完成
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], NULL);
    }
    
    printf("所有執行緒完成\n");
    
    // 帶共享資料的執行緒
    pthread_t counterThreads[2];
    int iterations = 1000;
    
    for (int i = 0; i < 2; i++) {
        pthread_create(&counterThreads[i], NULL, incrementCounter, &iterations);
    }
    
    for (int i = 0; i < 2; i++) {
        pthread_join(counterThreads[i], NULL);
    }
    
    printf("最終計數器值: %d\n", sharedCounter);
    
    return 0;
}
```
</UniversalEditor>

## 4. 信號處理

<UniversalEditor title="信號處理" compare={true}>
```javascript !! js
// JavaScript - 信號處理 (Node.js)
process.on('SIGINT', () => {
    console.log('收到 SIGINT (Ctrl+C)');
    console.log('正在清理...');
    process.exit(0);
});

process.on('SIGTERM', () => {
    console.log('收到 SIGTERM');
    console.log('優雅關閉...');
    process.exit(0);
});

process.on('uncaughtException', (error) => {
    console.error('未捕獲的異常:', error);
    process.exit(1);
});

// 自訂信號處理
let cleanupDone = false;

function cleanup() {
    if (!cleanupDone) {
        console.log('執行清理...');
        cleanupDone = true;
    }
}

process.on('exit', (code) => {
    cleanup();
    console.log(`行程退出，程式碼: ${code}`);
});

// 模擬一些工作
console.log('行程已啟動。按 Ctrl+C 停止。');
setInterval(() => {
    console.log('工作中...');
}, 1000);
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

volatile sig_atomic_t flag = 0;

// 信號處理函數
void signalHandler(int sig) {
    if (sig == SIGINT) {
        printf("\n收到 SIGINT (Ctrl+C)\n");
        printf("正在清理...\n");
        flag = 1;
    } else if (sig == SIGTERM) {
        printf("收到 SIGTERM\n");
        printf("優雅關閉...\n");
        flag = 1;
    }
}

// 清理函數
void cleanup() {
    printf("執行清理...\n");
    // 在此新增清理程式碼
}

int main() {
    // 設定信號處理器
    if (signal(SIGINT, signalHandler) == SIG_ERR) {
        perror("設定 SIGINT 處理器失敗");
        return 1;
    }
    
    if (signal(SIGTERM, signalHandler) == SIG_ERR) {
        perror("設定 SIGTERM 處理器失敗");
        return 1;
    }
    
    printf("行程已啟動。按 Ctrl+C 停止。\n");
    printf("行程 ID: %d\n", getpid());
    
    // 主迴圈
    while (!flag) {
        printf("工作中...\n");
        sleep(1);
    }
    
    cleanup();
    printf("行程退出\n");
    
    return 0;
}
```
</UniversalEditor>

## 5. 系統呼叫和檔案操作

<UniversalEditor title="系統呼叫" compare={true}>
```javascript !! js
// JavaScript - 檔案系統操作 (Node.js)
const fs = require('fs');
const path = require('path');

// 檔案操作
fs.writeFileSync('test.txt', '你好，世界！');
const content = fs.readFileSync('test.txt', 'utf8');
console.log('檔案內容:', content);

// 目錄操作
fs.mkdirSync('testdir', { recursive: true });
const files = fs.readdirSync('.');
console.log('當前目錄中的檔案:', files);

// 檔案統計
const stats = fs.statSync('test.txt');
console.log('檔案大小:', stats.size);
console.log('是檔案:', stats.isFile());
console.log('是目錄:', stats.isDirectory());

// 行程資訊
console.log('當前工作目錄:', process.cwd());
console.log('環境變數:', process.env.PATH);

// 清理
fs.unlinkSync('test.txt');
fs.rmdirSync('testdir');
```

```c !! c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>

int main() {
    // 使用系統呼叫進行檔案操作
    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open 失敗");
        return 1;
    }
    
    const char* message = "你好，世界！";
    ssize_t bytes_written = write(fd, message, strlen(message));
    if (bytes_written == -1) {
        perror("write 失敗");
        close(fd);
        return 1;
    }
    
    close(fd);
    
    // 讀取檔案
    fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("讀取時 open 失敗");
        return 1;
    }
    
    char buffer[100];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';
        printf("檔案內容: %s\n", buffer);
    }
    
    close(fd);
    
    // 檔案統計
    struct stat fileStat;
    if (stat("test.txt", &fileStat) == 0) {
        printf("檔案大小: %ld 位元組\n", fileStat.st_size);
        printf("是檔案: %s\n", S_ISREG(fileStat.st_mode) ? "是" : "否");
        printf("是目錄: %s\n", S_ISDIR(fileStat.st_mode) ? "是" : "否");
    }
    
    // 目錄操作
    DIR* dir = opendir(".");
    if (dir != NULL) {
        struct dirent* entry;
        printf("當前目錄中的檔案:\n");
        while ((entry = readdir(dir)) != NULL) {
            printf("  %s\n", entry->d_name);
        }
        closedir(dir);
    }
    
    // 行程資訊
    char cwd[1024];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("當前工作目錄: %s\n", cwd);
    }
    
    printf("行程 ID: %d\n", getpid());
    printf("父行程 ID: %d\n", getppid());
    
    // 清理
    unlink("test.txt");
    
    return 0;
}
```
</UniversalEditor>

## 6. 常見陷阱
- **競態條件**：使用適當的同步機制
- **信號處理器安全性**：在信號處理器中只使用非同步信號安全函數
- **資源洩漏**：始終關閉檔案描述符並釋放分配的記憶體
- **行程殭屍**：正確等待子行程
- **執行緒安全**：使用互斥鎖和其他同步原語

## 7. 練習題
1. 撰寫一個程式，建立多個子行程並等待所有行程完成。
2. 實現一個可以執行命令的簡單 shell。
3. 建立一個可以優雅處理多個信號的程式。
4. 撰寫一個執行並行計算的多執行緒程式。

## 8. 效能分析
- **行程建立**：比執行緒建立更昂貴
- **行程間通訊**：比執行緒間通訊更慢
- **系統呼叫**：與使用者空間操作相比有開銷
- **信號處理**：非同步且可能中斷正常執行
- **執行緒同步**：互斥鎖和信號量提供安全的並發存取

---

> **小結**：C 系統程式設計提供對作業系統功能的直接存取。理解行程管理、執行緒、信號處理和系統呼叫對建構系統級應用程式和工具至關重要。 